﻿/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * Disk Write Operation Class
 *		Performs the process of writing a byte pattern to disk.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace Adaptive.Foundation.IO.WipeDisk
{
    /// <summary>
    /// Performs the process of writing a byte pattern to disk.
    /// </summary>
    public sealed class DiskWriteOperation : IDisposable
    {
        /*------------------------------------------------------------------------
         * Public Events
         *----------------------------------------------------------------------*/
        #region Public Events
        /// <summary>
        /// Occurs as the write operation is being performed.
        /// </summary>
        public event ProgressEventHandler Update;
        #endregion

        /*------------------------------------------------------------------------
		 * Private Member Declarations
		 *----------------------------------------------------------------------*/
        #region Private Member Declarations

        private byte[] _Pattern;
        private byte[] _Data;
        private FileStream _File;
        private Thread _WriteThread;
        private DriveInfo _Drive;
        private long _MaxSize;
        private long _ByteCount;
        private long _BlockSize;
        private bool _Cancel;

        #endregion

        /*------------------------------------------------------------------------
		 * Constructor / Dispose / Destructor Methods
		 *----------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public DiskWriteOperation(DriveInfo drive, byte[] pattern)
        {
            _Pattern = pattern;
            _Drive = drive;
            CreateData();
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        public void Dispose()
        {
            if (_Pattern != null)
                Array.Clear(_Pattern, 0, _Pattern.Length);
            if (_Data != null)
                Array.Clear(_Data, 0, _Pattern.Length);

            _File = null;
            _Data = null;
            _Pattern = null;
            _WriteThread = null;
            _Drive = null;
            GC.SuppressFinalize(this);
        }
        #endregion

        /*------------------------------------------------------------------------
		 * Public Properties
		 *----------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets or sets the reference to the selected drive.
        /// </summary>
        /// <value>
        /// A <see cref="DriveInfo"/> instance.
        /// </value>
        public DriveInfo SelectedDrive
        {
            get { return _Drive; }
            set
            { _Drive = value; }
        }
        /// <summary>
        /// Gets or sets a value indicating whether the operation is to be 
        /// cancelled.
        /// </summary>
        public bool CancelOperation
        {
            get { return _Cancel; }
            set { _Cancel = value; }
        }
        /// <summary>
        /// Gets the current number of bytes written.
        /// </summary>
        /// <value>
        /// The number of bytes written.
        /// </value>
        public long Position
        {
            get { return _ByteCount; }
        }
        /// <summary>
        /// Gets the number of bytes that are to be written.
        /// </summary>
        /// <value>
        /// The total number of bytes to write.
        /// </value>
        public long MaxBytes
        {
            get { return _MaxSize; }
        }
        #endregion

        /*------------------------------------------------------------------------
		 * Public Methods / Functions
		 *----------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Launches the disk writing operation.
        /// </summary>
        public void BeginWriteOperation()
        {
            _WriteThread = new Thread(new ThreadStart(WriteOperation));
            _WriteThread.Name = "DiskWriteThread";
            _WriteThread.Priority = ThreadPriority.Highest;
            _WriteThread.Start();
        }
        /// <summary>
        /// Waits for the thread write operation to complete.
        /// </summary>
        public void EndWriteOperation()
        {
            do
            {
                Application.DoEvents();
                Thread.Sleep(10);
            } while ((_WriteThread != null) && (_WriteThread.IsAlive));
            _WriteThread = null;
        }
        #endregion

        /*------------------------------------------------------------------------
		 * Private Methods / Functions
		 *----------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Thread content.
        /// </summary>
        private void WriteOperation()
        {
            string fileName = string.Empty;
            int bufferSize = 1;
            int blockSize = (int)_BlockSize;

            //Open the file.
            fileName = _Drive.Name + "~CS.bin";

            if (File.Exists(fileName))
                File.Delete(fileName);

            _File = new FileStream(fileName, FileMode.CreateNew, FileAccess.Write,
                FileShare.None, bufferSize, false);

            //Write data blocks.
            _ByteCount = 0;
            DoUpdate();
            do
            {
                //Write content.
                try
                {
                    _File.Write(_Data, 0, blockSize);
                }
                catch (IOException)
                {
                }

                //Update the UI.
                _ByteCount += _BlockSize;
                DoUpdate();

            } while ((_ByteCount < _MaxSize) && (!_Cancel));

            DoUpdate();

            //Close and return.
            _File.Close();
            GC.Collect();
        }
        /// <summary>
        /// Creates the data to be written to disk.
        /// </summary>
        private void CreateData()
        {
            int count = 0;
            int position = 0;

            //Delete old file if still present.
            if (File.Exists(_Drive.Name + "~CS.bin"))
                File.Delete(_Drive.Name + "~CS.bin");

            //Create the markers and data container.
            _MaxSize = _Drive.TotalFreeSpace;
            _BlockSize = (long)((float)_MaxSize / 500) + 64;
            if (_BlockSize < 67108864)
                _BlockSize = 67108864;
            if (_BlockSize > _MaxSize)
                _BlockSize = _MaxSize / 2;

            _Data = new byte[_BlockSize];

            //Copy pattern data.
            for (count = 0; count < _BlockSize; count++)
            {
                _Data[count] = _Pattern[position];
                position++;
                if (position >= _Pattern.Length)
                    position = 0;
            }
        }
        /// <summary>
        /// Raises the <see cref="Update"/> event.
        /// </summary>
        /// <param name="e"></param>
        private void OnUpdate(ProgressEventArgs e)
        {
            if (Update != null)
                Update(this, e);
        }
        /// <summary>
        /// Updates the UI.
        /// </summary>
        private void DoUpdate()
        {
            ProgressEventArgs newArgs = null;
            int percent = 0;

            if (_ByteCount > _MaxSize)
                _ByteCount = _MaxSize;

            percent = (int)(((float)_ByteCount / _MaxSize) * 100);
            newArgs = new ProgressEventArgs("Writing Data...", percent);
            OnUpdate(newArgs);
            newArgs = null;
        }
        #endregion
    }
}
